﻿#define _CRT_SECURE_NO_WARNINGS

#include"snake.h"

void KeyFun()
{
	//检测是否有按键被按下
	while (_kbhit())
	{
		// 使用 _getch() 获取按下的键，不阻塞程序
		int key = _getch();
		// 处理按键事件，可以根据需要进行相应的操作
	}
}

//把设置光标位置的操作封装成为一个函数
void SetPos(int x, int y)
{
	//获得标准输出设备的句柄
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);

	//修改光标位置：COORD是win32自定义的关于光标在控制台的位置的结构体类型
	COORD pos = { x,y };

	//设置光标位置
	SetConsoleCursorPosition(houtput, pos);
}

//打印地图
void CreatMap()
{
	int i = 0;
	for (int i = 0; i <= 100; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	SetPos(0, 36);
	for (int i = 0; i <= 100; i += 2)
	{
		wprintf(L"%lc", WALL);
	}
	for (int i = 1; i <= 36; i++)
	{
		SetPos(0, i);
		wprintf(L"%lc", WALL);
	}
	for (int i = 1; i <= 36; i++)
	{
		SetPos(100, i);
		wprintf(L"%lc", WALL);
	}
}

//打印欢迎界面
void WelcomeToGame()
{
	//第一界面（欢迎界面）
	SetPos(62, 14);
	printf("欢迎来到贪吃蛇小游戏！");
	SetPos(64, 16);
	system("pause");
	system("cls");//清理屏幕
	//第二界面（游戏功能介绍）
	SetPos(50, 12);
	printf("1、用 ↑ . ↓ . ← . → 分别控制蛇的移动， F3为加速，F4为减速！");
	SetPos(50, 13);
	printf("2、加速将可以得到更高的分数！");
	SetPos(65, 15);
	system("pause");
	system("cls");//清理屏幕
}

//创建1个蛇身的节点
pSnakeNode CreatSnakeNode()
{
	pSnakeNode ret = (pSnakeNode)malloc(sizeof(SnakeNode));
	if (ret == NULL)
	{
		perror("CreatSnakeNode::fail\n");
		return NULL;
	}
	return ret;
}

//初始化蛇身
void InitSnake(pSnake ps)
{
	pSnakeNode cur = NULL;
	//创建5个蛇身的节点
	for (int i = 1; i <= 5; i++)
	{
		cur = CreatSnakeNode();
		cur->next = NULL;
		cur->x = POS_X + i * 2;
		cur->y = POS_Y;
		//将5个蛇身节点串起来
		if (ps->_pSnakeHead == NULL)
		{//蛇头为空就直接插入
			ps->_pSnakeHead = cur;
		}
		else//头插
		{
			cur->next = ps->_pSnakeHead;
			ps->_pSnakeHead = cur;
		}
	}
	cur = ps->_pSnakeHead;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	ps->_dri = RIGHT;//方向默认向右
	ps->_foodWeight = 10;
	ps->_score = 0;
	ps->_sleepTime = 200;
	ps->_state = OK;
}

//初始化食物
void CreateFood(pSnake ps)
{
	//先随机生成食物的坐标
	int x = 0;
	int y = 0;
again:
	//生成的位置必须在地图内部
	do
	{
		x = rand() % 97 + 2;
		y = rand() % 35 + 1;
	} while (x % 2 != 0);
	pSnakeNode cur = ps->_pSnakeHead;
	//判断食物位置是否与蛇身重叠
	while (cur)
	{
		if (cur->x == x && cur->y == y)
		{
			goto again;
		}
		cur = cur->next;
	}
	//打印食物
	SetPos(x, y);
	wprintf(L"%lc", FOOD);
	pSnakeNode food = CreatSnakeNode();
	food->x = x;
	food->y = y;
	food->next = NULL;
	//将食物节点放到ps中维护起来
	ps->_pFood = food;
}

//打印帮助手册
void PrintHelpInfo()
{
	SetPos(104, 16);
	printf("--------------------------------------------");
	SetPos(104, 17);
	printf("|1、不能撞墙，不能咬到自己！               |");
	SetPos(104, 18);
	printf("|2、用 ↑ . ↓ . ← . → 分别控制蛇的移动! |");
	SetPos(104, 19);
	printf("|3、F3为加速，F4为减速！                   |");
	SetPos(104, 20);
	printf("|4、按Esc退出游戏，按空格暂停游戏 !        |");
	SetPos(104, 21);
	printf("--------------------------------------------");
	SetPos(60, 17);
}

//游戏暂停
void Pause()
{
	while (1)
	{
		if (KEY_PRESS(VK_SPACE))
		{
			break;
		}
		Sleep(200);
		KeyFun();
	}
}

//吃掉食物
void EatFood(pSnakeNode pn, pSnake ps)
{
	//头插吃掉食物
	ps->_pFood->next = ps->_pSnakeHead;
	ps->_pSnakeHead = ps->_pFood;
	ps->_score += ps->_foodWeight;//分数增加
	//打印蛇身
	pSnakeNode cur = ps->_pSnakeHead;
	while (cur)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	//释放刚刚malloc的位置
	free(pn);
	pn = NULL;
	//食物被吃掉了，那就再创建一个食物
	CreateFood(ps);
}

//下一个位置不是食物
void NoFood(pSnake ps, pSnakeNode pn)
{
	//头插
	pn->next = ps->_pSnakeHead;
	ps->_pSnakeHead = pn;
	pSnakeNode cur = ps->_pSnakeHead;
	while (cur->next->next != NULL)
	{
		SetPos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	//把最后一个节点打印成两个空格
	SetPos(cur->next->x, cur->next->y);
	printf("  ");
	//释放最后一个节点
	free(cur->next);
	cur->next = NULL;
}

//蛇走一步
void SnakeMove(pSnake ps)
{
	//创建一个节点来记录蛇头的下一个位置
	pSnakeNode nextHead = CreatSnakeNode();
	nextHead->next = NULL;
	switch (ps->_dri)
	{
	case UP:
		nextHead->x = ps->_pSnakeHead->x;
		nextHead->y = ps->_pSnakeHead->y - 1;
		break;
	case DOWN:
		nextHead->x = ps->_pSnakeHead->x;
		nextHead->y = ps->_pSnakeHead->y + 1;
		break;
	case LEFT:
		nextHead->y = ps->_pSnakeHead->y;
		nextHead->x = ps->_pSnakeHead->x - 2;
		break;
	case RIGHT:
		nextHead->y = ps->_pSnakeHead->y;
		nextHead->x = ps->_pSnakeHead->x + 2;
		break;
	default:
		break;
	}
	//判断下一个节点是不是食物
	if ((ps->_pFood->x == nextHead->x) && (ps->_pFood->y == nextHead->y))
	{
		//下一个是食物那就吃掉食物
		EatFood(nextHead, ps);
	}
	else
	{
		//下一个位置不是食物
		NoFood(ps, nextHead);
	}
	//
	KillByWall(ps);
	KillBySelf(ps);
}

//检查是否撞墙
void KillByWall(pSnake ps)
{
	if (ps->_pSnakeHead->x == 0
		|| ps->_pSnakeHead->x == 98
		|| ps->_pSnakeHead->y == 0
		|| ps->_pSnakeHead->y == 36)
	{
		ps->_state = KILL_BY_WALL;
		return;
	}
	return;
}

//检查是否撞到自己
void KillBySelf(pSnake ps)
{
	pSnakeNode cur = ps->_pSnakeHead->next;
	while (cur)
	{
		if ((cur->x == ps->_pSnakeHead->x) &&
			(cur->y == ps->_pSnakeHead->y))
		{
			ps->_state = KILL_BY_SELF;
			return;
		}
		cur = cur->next;
	}
	return;
}

//游戏初始化
void GameStart(pSnake ps)
{
	//0. 先设置窗口的大小再隐藏光标
	system("mode con cols=150 lines=40");
	system("title 贪吃蛇");
	//获得标准输出设备的句柄
	HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
	//CONSOLE_CURSOR_INFO是win32自定义的一个结构体类型，里面包含了光标的比例和可见度
	CONSOLE_CURSOR_INFO cursorInfo = { 0 };
	//获得光标信息
	GetConsoleCursorInfo(houtput, &cursorInfo);
	//修改控制台的光标信息
	cursorInfo.dwSize = 100;//比例
	cursorInfo.bVisible = false;//可见度
	//设置光标的信息
	SetConsoleCursorInfo(houtput, &cursorInfo);

	//1. 打印欢迎界面和游戏功能介绍
	WelcomeToGame();
	//2. 绘制地图
	CreatMap();
	//3. 蛇身初始化
	InitSnake(ps);
	//4. 食物初始化
	CreateFood(ps);
}

//游戏运行
void GameRun(pSnake ps)
{
	PrintHelpInfo();
	do
	{
		SetPos(104, 12);
		printf("按空格键开始游戏！");
		SetPos(104, 13);
		printf("您当前的总分是%3d", ps->_score);
		SetPos(104, 14);
		printf("当前每个食物的总分是%3d", ps->_foodWeight);
		if (KEY_PRESS(VK_UP) && ps->_dri != DOWN)
		{
			ps->_dri = UP;//上
		}
		else if (KEY_PRESS(VK_DOWN) && ps->_dri != UP)
		{
			ps->_dri = DOWN;//下
		}
		else if (KEY_PRESS(VK_LEFT) && ps->_dri != RIGHT)
		{
			ps->_dri = LEFT;//左
		}
		else if (KEY_PRESS(VK_RIGHT) && ps->_dri != LEFT)
		{
			ps->_dri = RIGHT;//右
		}
		else if (KEY_PRESS(VK_SPACE))
		{
			Pause();//暂停
		}
		else if (KEY_PRESS(VK_ESCAPE))
		{
			ps->_state = END_NORMAL;//正常退出
		}
		else if (KEY_PRESS(VK_F3))
		{
			//加速
			if (ps->_sleepTime > 80)
			{
				ps->_sleepTime -= 30;
				ps->_foodWeight += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			//减速
			if (ps->_foodWeight > 2)
			{
				ps->_sleepTime += 30;
				ps->_foodWeight -= 2;
			}
		}
		//走一步
		SnakeMove(ps);
		//每走一步休息一下
		Sleep(ps->_sleepTime);

		KeyFun();

	} while (ps->_state == OK);
}

//游戏善后
void GameEnd(pSnake ps)
{
	pSnakeNode cur = ps->_pSnakeHead;
	while (cur)
	{
		pSnakeNode del = cur;
		cur = cur->next;
		free(del);
	}
}
